using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.UnityConverters;
using Newtonsoft.Json.UnityConverters.Math;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace BearUtil
{
    /// <summary>
    /// TexturePacker 命令行工具，因为美术 命名不够规范，文件夹层级也不够，采用 PlanB
    /// </summary>
    [InlineEditor, ExecuteInEditMode]
    public class TPCommandTool : BearScriptableObject
    {
        public bool isDev = false;
        public string TPPath;
        [FolderPath] public string OutputPath;
        [FolderPath] public string[] IgnorePaths;
        [Sirenix.OdinInspector.FilePath] public string TPSSetting;
        private string resPath = "sprite";

        #region Init

        /// <summary>
        /// 固定生成路径
        /// </summary>
        public override string DefaultPath
        {
            get => "Assets/TPCommand.asset";
        }

        /// <summary>
        /// 单例对象，快速获取
        /// </summary>
        private static TPCommandTool _instance;

        public static TPCommandTool Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = Init();
                }

                return _instance;
            }
        }

        // 用于Tools 手动创建对象，前期测试使用
        [MenuItem("Tools/BearTool/TP Command")]
        private static void menuOption()
        {
            generate<TPCommandTool>();
        }

        /// <summary>
        /// 初始化，新工具可以按照这个模板来重新写
        /// </summary>
        /// <returns></returns>
        public static TPCommandTool Init()
        {
            var instance = ScriptableObject.CreateInstance<TPCommandTool>();
            instance = AssetDatabase.LoadAssetAtPath<TPCommandTool>(instance.DefaultPath);
            if (instance == null)
            {
                TPCommandTool.menuOption();
                instance = AssetDatabase.LoadAssetAtPath<TPCommandTool>(instance.DefaultPath);
            }

            TPCommandTool._instance = instance;

            return instance;
        }

        #endregion

        // 相关文件地址信息
        [HideInInspector] public List<PathData> pathDatas = new List<PathData>();
        [HideInInspector] public List<TPData> tpDatas = new List<TPData>();

        /// <summary>
        /// PlanB，目标地址
        /// </summary>
        [LabelText("检索路径"), FolderPath] public string targetPath;

        [LabelText("最大顶点数"), HideInInspector] public int MaxPointCount = 30;

        [LabelText("顶点异常对象"), TableList, HideInInspector]
        public List<TPData> overPointsTargets = new List<TPData>();

        [LabelText("自动执行")] public bool isAuto = false;

        public bool isDebug = false;

        #region Generate TP Data

        private Process cmd;

        // 数据从第 16 行开始
        [NonSerialized] private int MinLine = 15;

        /// <summary>
        ///  尝试 1-5 atlas
        /// </summary>
        [NonSerialized] private int MaxTest = 10;


        // 分析数据
        private TPData parseRawData(string content)
        {
            TPData data = new TPData();
            try
            {
                var ps = content.Split(new string[] {"; "}, StringSplitOptions.None);

                // p1 data
                var p1 = ps[0].Split(';');
                data.name = p1[0];
                data.offset = new Vector2Int(Convert.ToInt16(p1[1]), Convert.ToInt16(p1[2]));
                data.size = new Vector2Int(Convert.ToInt16(p1[3]), Convert.ToInt16(p1[4]));

                // anchor
                var p2 = ps[1].Split(';');
                data.anchor = new Vector2(Convert.ToSingle(p2[0]), Convert.ToSingle(p2[1]));

                // pivot
                var p3 = ps[2].Split(';');
                data.padding = new Vector4(Convert.ToInt16(p3[0]), Convert.ToInt16(p3[1]), Convert.ToInt16(p3[2]),
                    Convert.ToInt16(p3[3]));

                // vertices
                var p4 = ps[3].Split(';');
                int count = Convert.ToInt16(p4[0]);
                data.vertices = new Vector2[count];
                for (int i = 0; i < count; i++)
                {
                    data.vertices[i] = new Vector2(Convert.ToInt16(p4[1 + 2 * i]), Convert.ToInt16(p4[2 + 2 * i]));
                }

                // indices
                var p5 = ps[4].Split(';');
                data.indices = new ushort[p5.Length - 1];
                for (int i = 1; i < p5.Length; i++)
                {
                    data.indices[i - 1] = Convert.ToUInt16(p5[i]);
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                Debug.LogError(data.name);
                isAuto = false;
            }

            return data;
        }

        private void initCmd()
        {
            cmd = new Process();
            cmd.StartInfo.FileName = TPPath;

            cmd.StartInfo.CreateNoWindow = true;
            cmd.StartInfo.UseShellExecute = false;
            cmd.StartInfo.RedirectStandardInput = true; // 重定向输入     
            cmd.StartInfo.RedirectStandardOutput = true; // 重定向标准输出
            cmd.StartInfo.StandardOutputEncoding = Encoding.UTF8;
        }

        private void execute(string spritesPath, string outputPath, Vector2 size)
        {
            if (cmd == null)
                return;
            // 重定向 error 输出
            cmd.StartInfo.RedirectStandardError = isDebug;

            // 规避 cmd space 问题
            cmd.StartInfo.Arguments = TPSSetting +
                                      " \"" + spritesPath + "\"" +
                                      " --width " + size.x + " --height " + size.y + 
                                      " --data " + " \"" + outputPath + "\"";
            cmd.Start();
            if (isDebug)
                Debug.LogError(cmd.StandardError.ReadToEnd());
            cmd.WaitForExit();
        }

        private void closeCmd()
        {
            if (cmd == null)
                return;
            cmd.Close();
        }

        [Button("ReImport Folder", ButtonSizes.Large)]
        private void ReimportFolder()
        {
            try
            {
                isAuto = true;
                var pngs = Directory.GetFiles(Application.dataPath.Replace("Assets", targetPath), "**.png",
                    SearchOption.AllDirectories).Where((png) =>
                {
                    for (int i = 0; i < IgnorePaths.Length; i++)
                    {
                        if (!string.IsNullOrEmpty(IgnorePaths[i]) && png.Contains(IgnorePaths[i]))
                            return false;
                    }

                    return true;
                }).ToArray();

                for (int i = 0; i < pngs.Length; i++)
                {
                    var png = pngs[i];
                    var relaPath = png.Replace(@"\", @"/").Replace(Application.dataPath, "Assets");


                    var importer = AssetImporter.GetAtPath(relaPath);
                    var textureSettings = new TextureImporterSettings();
                    (importer as TextureImporter).ReadTextureSettings(textureSettings);
                    // FullRect 不能重新设置
                    if (textureSettings.spriteMeshType == SpriteMeshType.Tight)
                        importer.SaveAndReimport();
                }

                isAuto = false;
                closeCmd();
            }
            catch (Exception t)
            {
                isAuto = false;
                Debug.LogError(t);
            }
        }

        [Button("Clear Cache", ButtonSizes.Large)]
        public void ClearAllAtlas()
        {
            var destRelaPath = $"{OutputPath}";
            var destPath = Application.dataPath.Replace("Assets", destRelaPath);

            if (!Directory.Exists(destPath))
            {
                Directory.CreateDirectory(destPath);
                return;
            }

            if (EditorUtility.DisplayDialog("冷静期", "确定清除吗？", "ok", "no"))
            {
                new DirectoryInfo(destPath).GetFiles().ForEach(file => file.Delete());
            }
        }

        /// <summary>
        /// 获取 TP 数据
        /// </summary>
        /// <param name="sprite"></param>
        public TPData GetTPData(string assetPath, Sprite sprite)
        {
            if (cmd == null)
                initCmd();

            var targetPath = assetPath.Replace(@"^Assets", Application.dataPath);
            var output = $"{OutputPath}/{Path.GetFileNameWithoutExtension(targetPath)}.tpsheet";

            TPData tData = null;
            // generate tpsheet                
            if (!File.Exists(output))
            {
                var realSize = new Vector2(sprite.texture.width.Format4(), sprite.texture.height.Format4());
                execute(targetPath, output, realSize);
            }

            var sheetFile = Application.dataPath.Replace("Assets", output);
            using (StreamReader sr = new StreamReader(sheetFile))
            {
                int line = 0;
                string rawLine;
                while (!string.IsNullOrEmpty(rawLine = sr.ReadLine()) || line < MinLine)
                {
                    if (line++ < MinLine)
                        continue;

                    // parse row data 
                    tData = parseRawData(rawLine);
                    tData.AssetPath = assetPath;
                    break;
                }
            }

            return tData;
        }

        #endregion
    }

    public class SpriteImport : AssetPostprocessor
    {
        private void OnPostprocessSprites(Texture2D texture, Sprite[] sprites)
        {
            try
            {
                if (!TPCommandTool.Instance.isAuto)
                    return;

                var importer = AssetImporter.GetAtPath(assetPath);
                var txtImporter = (importer as TextureImporter);

                TPData tData = null;
                var sprite = sprites[0];
                var data = importer.userData;
                Vector2[] vertices;
                var offset = new Vector2Int((int) sprite.textureRectOffset.x, (int) sprite.textureRectOffset.y);
                Vector2 rawSize = new Vector2(texture.width, texture.height);

                if (string.IsNullOrEmpty(data))
                {
                    // 直接结束
                    if (string.IsNullOrEmpty(TPCommandTool.Instance.TPPath) || !TPCommandTool.Instance.isDev)
                        return;

                    tData = TPCommandTool.Instance.GetTPData(assetPath, sprites[0]);
                    if (tData == null)
                        return;

                    var settings = new JsonSerializerSettings
                    {
                        Converters = new[]
                        {
                            new Vector3Converter()
                        },
                        ContractResolver = new UnityTypeContractResolver(),
                    };

                    importer.userData = JsonConvert.SerializeObject(tData, settings);
                    importer.SaveAndReimport();
                    vertices = SwitchVertices(tData, offset, rawSize);
                    sprite.OverrideGeometry(vertices, tData.indices);
                    return;
                }
                else
                {
                    try
                    {
                        tData = JsonConvert.DeserializeObject<TPData>(data);
                    }
                    catch (Exception e)
                    {
                        Debug.LogError(e);
                    }
                }

                vertices = SwitchVertices(tData, offset, rawSize);
                sprite.OverrideGeometry(vertices, tData.indices);
                AssetDatabase.Refresh();
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }

        // 获取优化后 vertices
        private Vector2[] SwitchVertices(TPData tData, Vector2Int offset, Vector2 size)
        {
            Vector2[] tmp = new Vector2[tData.vertices.Length];

            if (TPCommandTool.Instance.isDebug && size != tData.size)
            {
                Debug.LogError("[TPCommand] Size Different: " + tData.name);
            }

            // role 角色图片大小与产出不符
            for (int i = 0; i < tData.vertices.Length; i++)
            {
                var tmpV1 = tData.vertices[i] + offset;
                tmpV1.x = Mathf.Min(size.x, tmpV1.x);
                tmpV1.y = Mathf.Min(size.y, tmpV1.y);
                tmp[i] = tmpV1;
            }

            return tmp;
        }
    }


    /// <summary>
    /// TextPacker Data
    /// </summary>
    public class TPData
    {
        public string name;
        public Vector2Int size;
        public Vector2Int offset;
        public Vector2 anchor;
        public Vector4 padding;

        public Vector2[] vertices;
        public ushort[] indices;
        public string AssetPath;
    }

    /// <summary>
    /// 记录 path 数据
    /// </summary>
    [SerializeField]
    public class PathData
    {
        public string ABS;
        public string RELA;
        public string EXT;

        public string FileAssetPath;
        public string FileName;

        public string FolderName;

        // 父节点某一个文件夹名称
        public string ParentFolderName;
    }

    public static class Number4Format
    {
        public static int Format4(this int number)
        {
            var de = 4 - number % 4;
            if (de == 4)
                return number;

            number += de;
            return number;
        }
    }
}